package com.cy.device.service;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cy.device.cache.CyCabinetCache;
import com.cy.device.common.DeviceStatus;
import com.cy.device.common.MqttConstants;
import com.cy.device.common.RabbitmqConstants;
import com.cy.device.common.ResponseBean;
import com.cy.device.config.CyCabinetProperties;
import com.cy.device.model.*;
import com.cy.device.model.dto.CabinetDataDTO;
import com.cy.device.model.dto.CyCabinetDTO;
import com.cy.device.model.dto.DeviceStatusDTO;
import com.cy.device.rpc.PinholeCameraImgFeignClient;
import com.cy.device.rpc.VideoFfmpegFeignClient;
import com.cy.device.rpc.model.DeviceWithProperty;
import com.cy.device.utils.RandomUtil;
import com.cy.swpc.common.cabinetServiceManage.dto.PinholeImgInfo;
import com.cy.swpc.common.domain.common.TMSErrorCodeMgr;
import com.cy.swpc.common.domain.model.ResultBean;
import com.cy.swpc.common.domain.model.ResultResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author: jiangwang
 * @created: 2023/10/23 9:49
 */
@Service
public class MqttMessageHandler implements MessageHandler {

    private final Logger logger = LoggerFactory.getLogger(MqttMessageHandler.class);

    @Value("${time.sync}")
    private Integer timeSync;

    @Autowired
    private MqttGateway mqttGateway;

    @Autowired
    private RabbitmqProducer rabbitmqProducer;

    @Autowired
    private CyCabinetProperties cyCabinetProperties;

    @Autowired
    private PinholeCameraImgFeignClient pinholeCameraImgFeignClient;

    @Autowired
    private VideoFfmpegFeignClient videoFfmpegFeignClient;

    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        try {
            String payload = message.getPayload().toString();
            String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
            logger.info("接受来自mqtt的订阅信息，topic：{}", topic);
            //离线上报
            if (topic.matches(".+/offline")) {
                statusReport(payload);
            }
            //状态上报
            else if (topic.matches(".+/status")) {
                deviceInfoReport(payload);
            }
            //开门抓拍上报
            else if (topic.matches(".+/captureReport")) {
                uploadCaptureImage(payload);
            }
            //事件上报
            else if (topic.matches(".+/eventReport")) {
                eventReport(payload);
            }
            //参数设置返回
            else if (topic.matches(".+/configSetAck")) {
                reSetResult(payload);
            }
            //参数查询返回
            else if (topic.matches(".+/configGetAck")) {
                reConfigGetResult(payload);
            }
            //控制项设置返回
            else if (topic.matches(".+/controlAck")) {
                reSetResult(payload);
            }
            //控制项查询返回
            else if (topic.matches(".+/controlQueryAck")) {
                reControlResult(payload);
            } else {
                logger.info("主题topic:{}，负载payload:{}", topic, payload);
            }
        } catch (Exception e) {
            logger.error("handleMessage 接受mqtt订阅消息异常：", e);
        }
    }

    /**
     * 设备状态上报
     *
     * @param payload
     */
    private void statusReport(String payload) {
        OfflineReport offlineReport = JSONUtil.toBean(payload, OfflineReport.class);
        logger.info("收到设备状态信息上报：{}", offlineReport);
        String devNo = offlineReport.getId();
        String type = offlineReport.getType();
        DeviceStatusDTO statusDTO = new DeviceStatusDTO();
        if (CyCabinetCache.DEV_NO_LIST.contains(devNo)) {
            List<DeviceStatusDTO> list = new ArrayList<>();
            statusDTO.setDevNo(devNo);
            statusDTO.setStatus("online".equals(type) ? DeviceStatus.ONLINE : DeviceStatus.OFFLINE);
            list.add(statusDTO);
            rabbitmqProducer.sendMessage(RabbitmqConstants.DEVICE_STATUS, JSONUtil.toJsonStr(list));
        }
    }

    /**
     * 设备信息上报
     *
     * @param payload
     */
    private void deviceInfoReport(String payload) {
        DeviceInfo deviceInfo = JSONUtil.toBean(payload, DeviceInfo.class);
        logger.info("收到设备信息上报：{}", deviceInfo);
        String devNo = deviceInfo.getId();
        for (DeviceWithProperty deviceWithProperty : CyCabinetCache.CY_CABINET_LIST) {
            String propertyDevNo = deviceWithProperty.getDevNo();
            if (devNo.equals(propertyDevNo)) {
                Long timestamp = deviceInfo.getTimestamp();
                //当上报时间与当前系统时间相差2分钟时，发送校时命令给机柜
                if ((System.currentTimeMillis() / 1000 - timestamp) > timeSync) {
                    proofTime(devNo);
                }

                List<CyCabinetDTO> list = new ArrayList<>();
                CabinetDataDTO cabinetDataDTO = new CabinetDataDTO();
                CyCabinetDTO cyCabinetDTO = new CyCabinetDTO();
                cyCabinetDTO.setOrgNo(deviceWithProperty.getOrgNo());
                cyCabinetDTO.setDevNo(devNo);
                cyCabinetDTO.setDevName(deviceInfo.getDesc());
                cyCabinetDTO.setVolt(deviceInfo.getVolt());
                cyCabinetDTO.setCurrent(deviceInfo.getCurrent());
                cyCabinetDTO.setArdState(deviceInfo.getArdState());
                cyCabinetDTO.setVccState(deviceInfo.getVccState());
                cyCabinetDTO.setBatVolt(deviceInfo.getBatVolt());
                cyCabinetDTO.setHumi(deviceInfo.getHumi());
                cyCabinetDTO.setTemp(deviceInfo.getTemp());
                cyCabinetDTO.setDoor(deviceInfo.getDoor());
                cyCabinetDTO.setBackdoor(deviceInfo.getBackdoor());
                cyCabinetDTO.setWater(deviceInfo.getWater());
                cyCabinetDTO.setSmoke(deviceInfo.getSmoke());
                cyCabinetDTO.setFan(deviceInfo.getFan());
                cyCabinetDTO.setLight(deviceInfo.getLight());
                cyCabinetDTO.setDc12_2(deviceInfo.getDc12_2());
                cyCabinetDTO.setDc12_3(deviceInfo.getDc12_3());
                cyCabinetDTO.setAc220(deviceInfo.getAc220());
                list.add(cyCabinetDTO);
                cabinetDataDTO.setProductCode(cyCabinetProperties.getProductCode());
                cabinetDataDTO.setDevType(cyCabinetProperties.getDevType());
                cabinetDataDTO.setData(list);
                JSONObject jsonObject = JSONUtil.parseObj(cabinetDataDTO, false, true);
                //向mq发送设备状态信息
                rabbitmqProducer.sendMessage(RabbitmqConstants.DEVICE_DATA, jsonObject.toStringPretty());

                //每上报一次状态信息，就推送一次设备在线状态消息
                DeviceStatusDTO statusDTO = new DeviceStatusDTO();
                statusDTO.setDevNo(devNo);
                statusDTO.setStatus(DeviceStatus.ONLINE);
                List<DeviceStatusDTO> deviceStatus = new ArrayList<>();
                deviceStatus.add(statusDTO);
                rabbitmqProducer.sendMessage(RabbitmqConstants.DEVICE_STATUS, JSONUtil.toJsonStr(deviceStatus));
            }
        }
    }

    /**
     * 发送时间校时命令
     *
     * @param deviceNo
     */
    private void proofTime(String deviceNo) {
        //生成messageId
        int messageId = RandomUtil.generateRandomNumber();
        ControlSet controlSet = new ControlSet();
        controlSet.setMessageId(messageId);

        List<DeviceControlSet> options = new ArrayList<>();
        DeviceControlSet deviceControlSet = new DeviceControlSet();
        deviceControlSet.setName("timeSync");
        deviceControlSet.setState(System.currentTimeMillis() / 1000);
        options.add(deviceControlSet);
        controlSet.setOptions(options);

        String cmd = JSONUtil.parseObj(controlSet, false).toStringPretty();
        String topic = String.format(MqttConstants.DOWN_DEVICE_CONTROL, deviceNo);
        //发送指令
        mqttGateway.sendToMqtt(topic, 1, cmd);
        logger.info("向智能柜【{}】,发送控制项设置指令：{}", deviceNo, cmd);
    }

    /**
     * 上传开门抓拍图片
     *
     * @param payload
     */
    private void uploadCaptureImage(String payload) {
        logger.info("payload：{}", payload);
        CaptureReport captureReport = JSONUtil.toBean(payload, CaptureReport.class);
        logger.info("收到开门抓拍上报：{}", captureReport);
        //设备编号
        String devNo = captureReport.getId();
        if (CyCabinetCache.DEV_NO_LIST.contains(devNo)) {
            PinholeImgInfo pinholeImgInfo = new PinholeImgInfo();
            pinholeImgInfo.setDevIp("");
            pinholeImgInfo.setDevNo(devNo);
            List<ImageInfo> images = captureReport.getImages();
            for (ImageInfo image : images) {
                String data = image.getData();
                pinholeImgInfo.setImgStr(data);
                try {
                    pinholeCameraImgFeignClient.uploadImg(pinholeImgInfo);
                } catch (Exception e) {
                    logger.error("上传图片出错：", e);
                }
            }
        }
    }

    /**
     * 事件上报
     *
     * @param payload
     */
    private void eventReport(String payload) {
        EventReport eventReport = JSONUtil.toBean(payload, EventReport.class);
        logger.info("收到设备事件上报：{}", eventReport);
        String devNo = eventReport.getId();
        if (CyCabinetCache.DEV_NO_LIST.contains(devNo)) {
            Integer door = eventReport.getDoor();
            if (door != null && door == 1) {
                doorOpenedNew(devNo);
            }
            rabbitmqProducer.sendMessage(RabbitmqConstants.DEVICE_EVENT, JSONUtil.toJsonStr(eventReport));
        }
    }

    /**
     * 设置参数返回信息
     *
     * @param payload
     */
    private void reSetResult(String payload) {
        DeviceSetAck ack = JSONUtil.toBean(payload, DeviceSetAck.class);
        logger.info("智能机柜返回数据：{}", ack);
        Integer messageId = ack.getMessageId();
        if (DeviceService.resultMap.containsKey(messageId)) {
            DeferredResult<ResponseBean> deferredResult = DeviceService.resultMap.get(messageId);
            if (Boolean.TRUE.equals(ack.getSuccess())) {
                deferredResult.setResult(ResponseBean.success());
            } else {
                deferredResult.setResult(ResponseBean.error(ack.getMessage()));
            }
        }
    }

    /**
     * 参数查询返回信息
     *
     * @param payload
     */
    private void reConfigGetResult(String payload) {
        DeviceConfigGetAck ack = JSONUtil.toBean(payload, DeviceConfigGetAck.class);
        logger.info("参数查询返回信息：{}", ack);
        Integer messageId = ack.getMessageId();
        if (DeviceService.resultMap.containsKey(messageId)) {
            DeferredResult<ResponseBean> deferredResult = DeviceService.resultMap.get(messageId);
            if (Boolean.TRUE.equals(ack.getSuccess())) {
                deferredResult.setResult(ResponseBean.success(ack.getProperties()));
            } else {
                deferredResult.setResult(ResponseBean.error(ack.getMessage()));
            }
        }
    }

    /**
     * 查询控制项返回信息
     *
     * @param payload
     */
    private void reControlResult(String payload) {
        ControlQueryAck ack = JSONUtil.toBean(payload, ControlQueryAck.class);
        logger.info("控制项查询返回信息：{}", ack);
        Integer messageId = ack.getMessageId();
        if (DeviceService.resultMap.containsKey(messageId)) {
            DeferredResult<ResponseBean> deferredResult = DeviceService.resultMap.get(messageId);
            deferredResult.setResult(ResponseBean.success(ack.getOptions()));
        }
    }

    /**
     * 杰峰-摄像头
     *
     * @param devNo
     */
    private void doorOpenedNew(String devNo) {
        logger.info("杰峰摄像头抓拍:{}", devNo);
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            private Integer count = 0;

            @Override
            public void run() {
                if (count < 3) {
                    logger.info("开始获取抓拍照片:{}", devNo);
                    String base64 = "";
                    try {
                        // 获取图片
                        ResultBean<String> result = videoFfmpegFeignClient.getSnapPic(devNo);
                        if (result.getResultCode() != ResultBean.OK) {
                            logger.error("获取抓拍图片返回错误:{}", result.getResultMessage());
                        }
                        // base64
                        base64 = result.getData();
                    } catch (Exception e) {
                        logger.error("获取抓拍图片出错:", e);
                    }
                    if (StringUtils.hasLength(base64)) {
                        PinholeImgInfo pinholeImgInfo = new PinholeImgInfo();
                        pinholeImgInfo.setDevNo(devNo);
                        pinholeImgInfo.setDevIp("");
                        pinholeImgInfo.setImgStr(base64);
                        try {
                            ResultResponse uploadResult = pinholeCameraImgFeignClient.uploadImg(pinholeImgInfo);
                            if (TMSErrorCodeMgr.AH_COMMON_OK != uploadResult.getResultCode()) {
                                logger.error("上传抓拍照片失败：{}", uploadResult.getResultMessage());
                            } else {
                                logger.info("上传抓拍照片成功。");
                            }
                        } catch (Exception e) {
                            logger.error("上传抓拍图片出错:", e);
                        }
                    } else {
                        logger.error("获取抓拍图片为空！");
                    }
                    count++;
                } else {
                    cancel();// 取消任务
                }
            }
        }, 10, 2000);
    }

}
